// Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::{Path, PathBuf};

use anyhow::Context;
use clap::Parser;
use indicatif::MultiProgress;
use log::error;

use crate::{build::chroot::{chroot_cmd, chroot_mount_setup, pre_chroot_setup, umount_targets}, FerriumCommand};

#[derive(Debug, Parser)]
pub struct ChrootCommand {
    /// Change working directory before running command
    #[arg(short, long)]
    chdir: Option<PathBuf>,
    /// The target root path
    root: PathBuf,
    /// The command to run in chroot
    command: String,
    /// The arguments of the command
    args: Vec<String>,
}

impl FerriumCommand for ChrootCommand {
    async fn run(self, _multi_progress: MultiProgress) -> anyhow::Result<()> {
        let root = std::path::absolute(&self.root)
            .context(format!("failed to absolute '{}'", self.root.display()))?;
        pre_chroot_setup(&root, false, None, false, true, true).await
            .context("failed to setup for chroot")?;
        let mut mount_targets = chroot_mount_setup(&root).await
            .map_err(|_| anyhow::anyhow!("failed to setup chroot mount targets"))?;
        tokio::select! {
            _ = tokio::signal::ctrl_c() => {
                if !umount_targets(&root, &mut mount_targets).await {
                    return Err(anyhow::anyhow!("failed to clear chroot mount targets"));
                }
                return Err(anyhow::anyhow!("user interrupted"));
            },
            res = run_chroot(&root, self.chdir.as_ref().map(|v| &**v), &self.command, &self.args) => {
                if let Err(e) = res {
                    error!("{:?}", e);
                }
            },
        }

        if !umount_targets(&root, &mut mount_targets).await {
            return Err(anyhow::anyhow!("failed to clear chroot mount targets"));
        }
        Ok(())
    }
}

async fn run_chroot(root: &Path, chdir: Option<&Path>, command: &str, args: &[String]) -> anyhow::Result<()> {
    let mut cmd = chroot_cmd(&root, chdir.as_ref().map(|v| &**v), false, false);
    cmd.arg(command);
    cmd.args(args);
    let status = cmd
        .spawn()?
        .wait()
        .await?;
    if !status.success() {
        return Err(anyhow::anyhow!("chroot command failed with status {}", status));
    }
    Ok(())
}
